/* MacGuffin Cipher
* 10/3/94 Matt Blaze
* (fast, unrolled version)
*/

//
// Changed to conform to padded packets emulator by Jack Applegame
// $Id: mcg_cipher.c 5135 2006-11-18 22:06:15Z mouseland $
// 

#include "mcg_cipher.h"

#define TSIZE (1<<16)

#define IN00 0x0036
#define IN01 0x06C0
#define IN02 0x6900
#define OUT0 0x000F
#define IN10 0x5048
#define IN11 0x2106
#define IN12 0x8411
#define OUT1 0x00F0
#define IN20 0x8601
#define IN21 0x4828
#define IN22 0x10C4
#define OUT2 0x3300
#define IN30 0x2980
#define IN31 0x9011
#define IN32 0x022A
#define OUT3 0xCC00


word sBoxes[8][64]=
{
	{0x0002, 0x0000, 0x0000, 0x0003, 0x0003, 0x0001, 0x0001, 0x0000,
	 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0003, 0x0002, 0x0001,
	 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, 0x0002, 0x0003,
	 0x0001, 0x0003, 0x0003, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002,
	 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0000,
	 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001,
	 0x0003, 0x0001, 0x0002, 0x0003, 0x0003, 0x0001, 0x0001, 0x0002,
	 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003},
	{0x000C, 0x0004, 0x0004, 0x000C, 0x0008, 0x0000, 0x0008, 0x0004,
	 0x0000, 0x000C, 0x000C, 0x0000, 0x0004, 0x0008, 0x0000, 0x0008,
	 0x000C, 0x0008, 0x0004, 0x0000, 0x0000, 0x0004, 0x000C, 0x0008,
	 0x0008, 0x0000, 0x0000, 0x000C, 0x0004, 0x000C, 0x0008, 0x0004,
	 0x0000, 0x000C, 0x0008, 0x0008, 0x0004, 0x0008, 0x000C, 0x0004,
	 0x0008, 0x0004, 0x0000, 0x000C, 0x000C, 0x0000, 0x0004, 0x0000,
	 0x0004, 0x000C, 0x0008, 0x0000, 0x0008, 0x0004, 0x0000, 0x0008,
	 0x000C, 0x0000, 0x0004, 0x0004, 0x0000, 0x0008, 0x000C, 0x000C},
	{0x0020, 0x0030, 0x0000, 0x0010, 0x0030, 0x0000, 0x0020, 0x0030,
	 0x0000, 0x0010, 0x0010, 0x0000, 0x0030, 0x0000, 0x0010, 0x0020,
	 0x0010, 0x0000, 0x0030, 0x0020, 0x0020, 0x0010, 0x0010, 0x0020,
	 0x0030, 0x0020, 0x0000, 0x0030, 0x0000, 0x0030, 0x0020, 0x0010,
	 0x0030, 0x0010, 0x0000, 0x0020, 0x0000, 0x0030, 0x0030, 0x0000,
	 0x0020, 0x0000, 0x0030, 0x0030, 0x0010, 0x0020, 0x0000, 0x0010,
	 0x0030, 0x0000, 0x0010, 0x0030, 0x0000, 0x0020, 0x0020, 0x0010,
	 0x0010, 0x0030, 0x0020, 0x0010, 0x0020, 0x0000, 0x0010, 0x0020},
	{0x0040, 0x00C0, 0x00C0, 0x0080, 0x0080, 0x00C0, 0x0040, 0x0040,
	 0x0000, 0x0000, 0x0000, 0x00C0, 0x00C0, 0x0000, 0x0080, 0x0040,
	 0x0040, 0x0000, 0x0000, 0x0040, 0x0080, 0x0000, 0x0040, 0x0080,
	 0x00C0, 0x0040, 0x0080, 0x0080, 0x0000, 0x0080, 0x00C0, 0x00C0,
	 0x0080, 0x0040, 0x0000, 0x00C0, 0x00C0, 0x0000, 0x0000, 0x0000,
	 0x0080, 0x0080, 0x00C0, 0x0040, 0x0040, 0x00C0, 0x00C0, 0x0080,
	 0x00C0, 0x00C0, 0x0040, 0x0000, 0x0040, 0x0040, 0x0080, 0x00C0,
	 0x0040, 0x0080, 0x0000, 0x0040, 0x0080, 0x0000, 0x0000, 0x0080},
	{0x0000, 0x0200, 0x0200, 0x0300, 0x0000, 0x0000, 0x0100, 0x0200,
	 0x0100, 0x0000, 0x0200, 0x0100, 0x0300, 0x0300, 0x0000, 0x0100,
	 0x0200, 0x0100, 0x0100, 0x0000, 0x0100, 0x0300, 0x0300, 0x0200,
	 0x0300, 0x0100, 0x0000, 0x0300, 0x0200, 0x0200, 0x0300, 0x0000,
	 0x0000, 0x0300, 0x0000, 0x0200, 0x0100, 0x0200, 0x0300, 0x0100,
	 0x0200, 0x0100, 0x0300, 0x0200, 0x0100, 0x0000, 0x0200, 0x0300,
	 0x0300, 0x0000, 0x0300, 0x0300, 0x0200, 0x0000, 0x0100, 0x0300,
	 0x0000, 0x0200, 0x0100, 0x0000, 0x0000, 0x0100, 0x0200, 0x0100},
	{0x0800, 0x0800, 0x0400, 0x0C00, 0x0800, 0x0000, 0x0C00, 0x0000,
	 0x0C00, 0x0400, 0x0000, 0x0800, 0x0000, 0x0C00, 0x0800, 0x0400,
	 0x0000, 0x0000, 0x0C00, 0x0400, 0x0400, 0x0C00, 0x0000, 0x0800,
	 0x0800, 0x0000, 0x0400, 0x0C00, 0x0400, 0x0400, 0x0C00, 0x0800,
	 0x0C00, 0x0000, 0x0800, 0x0400, 0x0C00, 0x0000, 0x0400, 0x0800,
	 0x0000, 0x0C00, 0x0800, 0x0400, 0x0800, 0x0C00, 0x0400, 0x0800,
	 0x0400, 0x0C00, 0x0000, 0x0800, 0x0000, 0x0400, 0x0800, 0x0400,
	 0x0400, 0x0000, 0x0C00, 0x0000, 0x0C00, 0x0800, 0x0000, 0x0C00},
	{0x0000, 0x3000, 0x3000, 0x0000, 0x0000, 0x3000, 0x2000, 0x1000,
	 0x3000, 0x0000, 0x0000, 0x3000, 0x2000, 0x1000, 0x3000, 0x2000,
	 0x1000, 0x2000, 0x2000, 0x1000, 0x3000, 0x1000, 0x1000, 0x2000,
	 0x1000, 0x0000, 0x2000, 0x3000, 0x0000, 0x2000, 0x1000, 0x0000,
	 0x1000, 0x0000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x2000,
	 0x2000, 0x1000, 0x1000, 0x0000, 0x1000, 0x2000, 0x2000, 0x1000,
	 0x2000, 0x3000, 0x3000, 0x1000, 0x0000, 0x0000, 0x2000, 0x3000,
	 0x0000, 0x2000, 0x1000, 0x0000, 0x3000, 0x1000, 0x0000, 0x2000},
	{0xC000, 0x4000, 0x0000, 0xC000, 0x8000, 0xC000, 0x0000, 0x8000,
	 0x0000, 0x8000, 0xC000, 0x4000, 0xC000, 0x4000, 0x4000, 0x0000,
	 0x8000, 0x8000, 0xC000, 0x4000, 0x4000, 0x0000, 0x8000, 0xC000,
	 0x4000, 0x0000, 0x0000, 0x8000, 0x8000, 0xC000, 0x4000, 0x0000,
	 0x4000, 0x0000, 0xC000, 0x4000, 0x0000, 0x8000, 0x4000, 0x4000,
	 0xC000, 0x0000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0000, 0xC000,
	 0x0000, 0xC000, 0x0000, 0x8000, 0x8000, 0xC000, 0xC000, 0x0000,
	 0xC000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0000, 0x8000, 0xC000}
};

word sTable[TSIZE];

void MCGInit(void)
{
	dword i,j;
	static int sBits[8][6]=  // p69E404 (p69E414 - 0x10)
	{
		{0x2, 0x5, 0x6, 0x9, 0xB , 0xD},
		{0x1, 0x4, 0x7, 0xA, 0x8, 0xE},
		{0x3, 0x6, 0x8, 0xD, 0x0, 0xF},
		{0xC, 0xE, 0x1, 0x2, 0x4, 0xA},
		{0x0, 0xA, 0x3, 0xE, 0x6, 0xC},
		{0x7, 0x8, 0xC, 0xF, 0x1, 0x5},
		{0x9, 0xF, 0x5, 0xB, 0x2, 0x7},
		{0xB, 0xD, 0x0, 0x4, 0x3, 0x9}
	};
	for(i = 0; i<TSIZE; i++)
	{
		sTable[i] = 0;
		for(j = 0; j < 8; j++)
			sTable[i] |=
			sBoxes[j][((i >> sBits[j][0]) & 1)
							|(((i >> sBits[j][1]) & 1) << 1)
							|(((i >> sBits[j][2]) & 1) << 2)
							|(((i >> sBits[j][3]) & 1) << 3)
							|(((i >> sBits[j][4]) & 1) << 4)
							|(((i >> sBits[j][5]) & 1) << 5)];
	}
}

void MCGKeyset(byte* Key, MCGKey* eKey)
{
	int i,j;
	byte k[2][8];
	MCGInit();
	for(i=0;i<8;i++)
	{
		k[0][i] = Key[i];
		k[1][i] = Key[i+8];
	}
	for(i=0;i<KSIZE;i++) eKey->Val[i] = 0;
	for(i=0;i<2;i++)
		for(j=0;j<32;j++)
		{
			MCGBlockEncrypt0(k[i],eKey);
			eKey->Val[j*3+0] ^= ((word*)k[i])[0];
			eKey->Val[j*3+1] ^= ((word*)k[i])[1];
			eKey->Val[j*3+2] ^= ((word*)k[i])[2];
		}
}

void MCGBlockEncrypt0(byte* Blk, MCGKey* eKey)
{
	word r0, r1, r2, r3, a, b, c;
	int i;
	word *ek = eKey->Val;
	r0 = ((word*)Blk)[0];
	r1 = ((word*)Blk)[1];
	r2 = ((word*)Blk)[2];
	r3 = ((word*)Blk)[3];
	for(i=0; i<ROUNDS/4; i++)
	{
		a = r1 ^ *(ek++); b = r2 ^ *(ek++); c = r3 ^ *(ek++);
		r0 ^= ((OUT0 & sTable[(a & IN00) | (b & IN01) | (c & IN02)])
					|(OUT1 & sTable[(a & IN10) | (b & IN11) | (c & IN12)])
					|(OUT2 & sTable[(a & IN20) | (b & IN21) | (c & IN22)])
					|(OUT3 & sTable[(a & IN30) | (b & IN31) | (c & IN32)]));
		a = r2 ^ *(ek++); b = r3 ^ *(ek++); c = r0 ^ *(ek++);
		r1 ^= ((OUT0 & sTable[(a & IN00) | (b & IN01) | (c & IN02)])
					|(OUT1 & sTable[(a & IN10) | (b & IN11) | (c & IN12)])
					|(OUT2 & sTable[(a & IN20) | (b & IN21) | (c & IN22)])
					|(OUT3 & sTable[(a & IN30) | (b & IN31) | (c & IN32)]));
		a = r3 ^ *(ek++); b = r0 ^ *(ek++); c = r1 ^ *(ek++);
		r2 ^= ((OUT0 & sTable[(a & IN00) | (b & IN01) | (c & IN02)])
					|(OUT1 & sTable[(a & IN10) | (b & IN11) | (c & IN12)])
					|(OUT2 & sTable[(a & IN20) | (b & IN21) | (c & IN22)])
					|(OUT3 & sTable[(a & IN30) | (b & IN31) | (c & IN32)]));
		a = r0 ^ *(ek++); b = r1 ^ *(ek++); c = r2 ^ *(ek++);
		r3 ^= ((OUT0 & sTable[(a & IN00) | (b & IN01) | (c & IN02)])
					|(OUT1 & sTable[(a & IN10) | (b & IN11) | (c & IN12)])
					|(OUT2 & sTable[(a & IN20) | (b & IN21) | (c & IN22)])
					|(OUT3 & sTable[(a & IN30) | (b & IN31) | (c & IN32)]));
	}
	((word*)Blk)[0] = r0;
	((word*)Blk)[1] = r1;
	((word*)Blk)[2] = r2;
	((word*)Blk)[3] = r3;
}

void MCGBlockEncrypt1(byte* Blk, MCGKey* eKey)
{
	word r0, r1, r2, r3, a, b, c;
	int i;
	word *ek = eKey->Val+KSIZE-1;
	r0 = ((word*)Blk)[1];
	r1 = ((word*)Blk)[2];
	r2 = ((word*)Blk)[3];
	r3 = ((word*)Blk)[0];
	for(i=0; i<ROUNDS/4; i++)
	{
		//di - c; cx - b; si - a;
		c = r1 ^ *(ek--); b = r0 ^ *(ek--); a = r3 ^ *(ek--);
		r2 ^= ((OUT0 & sTable[(a & IN00)|(b & IN01)|(c & IN02)])
					|(OUT1 & sTable[(a & IN10)|(b & IN11)|(c & IN12)])
					|(OUT2 & sTable[(a & IN20)|(b & IN21)|(c & IN22)])
					|(OUT3 & sTable[(a & IN30)|(b & IN31)|(c & IN32)]));
		c = r0 ^ *(ek--); b = r3 ^ *(ek--); a = r2 ^ *(ek--);
		r1 ^= ((OUT0 & sTable[(a & IN00)|(b & IN01)|(c & IN02)])
					|(OUT1 & sTable[(a & IN10)|(b & IN11)|(c & IN12)])
					|(OUT2 & sTable[(a & IN20)|(b & IN21)|(c & IN22)])
					|(OUT3 & sTable[(a & IN30)|(b & IN31)|(c & IN32)]));
		c = r3 ^ *(ek--); b = r2 ^ *(ek--); a = r1 ^ *(ek--);
		r0 ^= ((OUT0 & sTable[(a & IN00)|(b & IN01)|(c & IN02)])
					|(OUT1 & sTable[(a & IN10)|(b & IN11)|(c & IN12)])
					|(OUT2 & sTable[(a & IN20)|(b & IN21)|(c & IN22)])
					|(OUT3 & sTable[(a & IN30)|(b & IN31)|(c & IN32)]));
		c = r2 ^ *(ek--); b = r1 ^ *(ek--); a = r0 ^ *(ek--);
		r3 ^= ((OUT0 & sTable[(a & IN00)|(b & IN01)|(c & IN02)])
					|(OUT1 & sTable[(a & IN10)|(b & IN11)|(c & IN12)])
					|(OUT2 & sTable[(a & IN20)|(b & IN21)|(c & IN22)])
					|(OUT3 & sTable[(a & IN30)|(b & IN31)|(c & IN32)]));
	}
	((word*)Blk)[1] = r0;
	((word*)Blk)[2] = r1;
	((word*)Blk)[3] = r2;
	((word*)Blk)[0] = r3;
}
